#region License
/*

	Copyright (C) 2008 Oliver Charles, Daniel Hollander

	This program is free software: you can redistribute it and/or modify
	it under the terms of the GNU General Public License as published by
	the Free Software Foundation, either version 3 of the License, or
	(at your option) any later version.

	This program is distributed in the hope that it will be useful,
	but WITHOUT ANY WARRANTY; without even the implied warranty of
	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
	GNU General Public License for more details.

	You should have received a copy of the GNU General Public License
	along with this program.  If not, see <http://www.gnu.org/licenses/>.
	
*/
#endregion

using System.Runtime.InteropServices;
using Moebius.Components;
using System.Collections.Generic;
using System;
using System.Drawing;
using Tao.OpenGl;

namespace Moebius.Rendering.OpenGL
{
	[StructLayout(LayoutKind.Sequential)]
    struct Vertex
    {
		public float Px, Py, Pz, Tu, Tv;

        public static int SizeBytes
        {
            get { return Marshal.SizeOf(typeof(Vertex)); }
        }

        public Vertex(float x, float y, float z, float u, float v)
        {
            Px = x;
			Py = y;
			Pz = z;
			Tu = u;
			Tv = v;
        }
    }
	
	public sealed class Image : Components.Image
	{
		private IntPtr vertexBufferObject, indexBufferObject;
		private int[] buffers = {0, 0};
		private int quadCounter;

		public override void Initialize(int initialQuadCount, float zLayer)
		{
			base.Initialize(initialQuadCount, zLayer);
			
			Gl.glGenBuffersARB(2, buffers);

			Gl.glBindBuffer(Gl.GL_ARRAY_BUFFER_ARB, buffers[0]);
			Gl.glBufferDataARB(Gl.GL_ARRAY_BUFFER_ARB, (IntPtr)(4 * initialQuadCount * Vertex.SizeBytes),
				0, Gl.GL_DYNAMIC_DRAW_ARB);
			
			Gl.glBindBuffer(Gl.GL_ELEMENT_ARRAY_BUFFER_ARB, buffers[1]);
			Gl.glBufferDataARB(Gl.GL_ELEMENT_ARRAY_BUFFER_ARB, (IntPtr)(6 * initialQuadCount * sizeof(short)),
				0, Gl.GL_DYNAMIC_DRAW_ARB);
			
			Gl.glBindBuffer(Gl.GL_ELEMENT_ARRAY_BUFFER_ARB, 0);
			Gl.glBindBuffer(Gl.GL_ARRAY_BUFFER_ARB, 0);
		}
		
		public override void AddQuad(RectangleF rectangle, string imageIdentifier)
		{
			RectangleF uvCoords = ImageAtlas.GetUVCoordinates(imageIdentifier);

            Vertex[] rawVerts = {
								   new Vertex(rectangle.Left, rectangle.Top, Layer, uvCoords.Left, uvCoords.Top),
                                   new Vertex(rectangle.Left, rectangle.Bottom, Layer, uvCoords.Left, uvCoords.Bottom),
								   new Vertex(rectangle.Right, rectangle.Top, Layer, uvCoords.Right, uvCoords.Top),
								   new Vertex(rectangle.Right, rectangle.Bottom, Layer, uvCoords.Right, uvCoords.Bottom)
							   };

			// Setup vertex buffer
			Gl.glBindBuffer(Gl.GL_ARRAY_BUFFER_ARB, buffers[0]);
			Gl.glBufferSubDataARB(Gl.GL_ARRAY_BUFFER_ARB, (IntPtr)(quadCounter * 4 * Vertex.SizeBytes),
				(IntPtr)(4 * Vertex.SizeBytes), rawVerts);
				
			Gl.glBindBuffer(Gl.GL_ARRAY_BUFFER_ARB, 0);
			
			// Setup index buffer
			ushort[] indicesData = new ushort[]
                {
                    (ushort)(quadCounter * 4 + 0), (ushort)(quadCounter * 4 + 1), (ushort)(quadCounter * 4 + 2),
                    (ushort)(quadCounter * 4 + 1), (ushort)(quadCounter * 4 + 2), (ushort)(quadCounter * 4 + 3)
                };
			
			Gl.glBindBuffer(Gl.GL_ELEMENT_ARRAY_BUFFER_ARB, buffers[1]);
			
			Gl.glBufferSubDataARB(Gl.GL_ELEMENT_ARRAY_BUFFER_ARB,
				(IntPtr)(quadCounter*6*sizeof(ushort)),
				(IntPtr)(6 * sizeof(ushort)), indicesData);
				
			Gl.glBindBuffer(Gl.GL_ELEMENT_ARRAY_BUFFER_ARB, 0);
			
			quadCounter++;
		}
		
		internal void Draw()
		{		
			ImageAtlas.Activate();
			
			Gl.glBindBufferARB(Gl.GL_ARRAY_BUFFER_ARB, buffers[0]);
			Gl.glBindBufferARB(Gl.GL_ELEMENT_ARRAY_BUFFER_ARB, buffers[1]);
			
			Gl.glTexCoordPointer(2, Gl.GL_FLOAT, Vertex.SizeBytes, (IntPtr)(sizeof(float) * 3));
			Gl.glVertexPointer(3, Gl.GL_FLOAT, Vertex.SizeBytes, (IntPtr) 0);
			
			Gl.glEnableClientState(Gl.GL_VERTEX_ARRAY);
			Gl.glEnableClientState(Gl.GL_TEXTURE_COORD_ARRAY);
			
			Gl.glDrawElements(Gl.GL_TRIANGLES, quadCounter * 6, Gl.GL_UNSIGNED_SHORT, (IntPtr) 0);
			
			Gl.glDisableClientState(Gl.GL_VERTEX_ARRAY);
			Gl.glDisableClientState(Gl.GL_TEXTURE_COORD_ARRAY);
			
			Gl.glBindBufferARB(Gl.GL_ARRAY_BUFFER_ARB, 0);
			Gl.glBindBufferARB(Gl.GL_ELEMENT_ARRAY_BUFFER_ARB, 0);
		}
	}
}